﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Migrator.Packets;
using Migrator.Util;
using System.Drawing;
using System.Diagnostics;

namespace Migrator.Entities
{
    /// <summary>
    /// Internet is a top-level router that routes messages
    /// from one network to another
    /// </summary>
    public sealed class Internet : Router
    {
        public Internet()
            : base(new MACAddress("FF:FF:FF:FF:FF:FF"), new IPAddress("0.0.0.1"), null, new PointF(0, 0))
        {
            // Internet provides no services.
            this.services.Clear();
        }

        public override void Update()
        {
            // Update each connected node
            foreach (Node node in nodes.Values)
            {
                node.Update();
            }

            this.ProcessReceivedPackets();

            // Process deregistration requests
            while (nodesToRemove.Count > 0)
            {
                this.nodes.Remove(nodesToRemove.Dequeue());
            }
        }

        public override void Send(IPPacket packet)
        {
            // Is it a broadcast message?
            if (packet.DestinationIP == IPAddress.BroadcastAddress)
            {
                this.Broadcast(packet);
            }
            else
            {
                // Otherwise, find the correct router and forward the message.
                foreach (var node in this.nodes.Values)
                {
                    if (node is Router)
                    {
                        Router router = (Router)node;
                        if (packet.DestinationIP.IsInNetwork(router.NetworkAddress))
                        {
                            this.SendLinkLayer(packet, router.MACAddress);
                            break;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Enqueues a new message in the inbox
        /// </summary>
        /// <param name="message">The message received</param>
        public override void Enqueue(IPPacket message)
        {
            // Adds the message to the inbox, to be handled in
            // the current iteration. Thus, there is no delay
            // in sending messages to the Internet.
            if (!this.inbox.ContainsKey(0))
                this.inbox[0] = new List<IPPacket>();

            this.inbox[0].Add(message);
        }

        public List<Router> GetRoutersInRange(PointF location)
        {
            return this.GetNodesInRange(location).Where(node => node is Router).Select(node => (Router) node).ToList();
        }

        /// <summary>
        /// The distance from the Internet to some other node is always 0
        /// </summary>
        /// <param name="other">The other node</param>
        /// <returns>0</returns>
        public override double DistanceTo(Node other)
        {
            return 0;
        }

        protected override void ProcessPacket(IPPacket packet)
        {
            //Debug.WriteLine(">>> " + this.ToString() + " received " + packet);

            // Dummy internet, always forwards what it receives
            this.Send(packet);
        }

        /// <summary>
        /// Broadcasts a given packet to any nodes in range
        /// </summary>
        /// <param name="packet">The packet to broadcast</param>
        private void Broadcast(IPPacket packet)
        {
            IEnumerable<Node> nodes = this.GetNodesInRange(packet.Source.Location);
            
            foreach (var node in nodes)
            {
                if (node != packet.Source)
                {
                    this.SendLinkLayer(packet, node.MACAddress);
                }
            }
        }
    }
}
